#include <string.h>

#include "lib.h"
#include "pkg.h"
#include "pathname.h"
#include "pkgmfest.h"
#include "mfestprv.h"
#include "apppath.h"
#include "dir.h"
#include "stream.h"
//
// ----------------------------- Package
//

Package::~Package()
{
	clearDepends();
}

void
Package::copy(Package& Src)
{
	name = Src.name;
	dllname = Src.dllname;

	zipsize = Src.zipsize;
	unzipsize = Src.unzipsize;
	localversion = Src.localversion;
	serverversion = Src.serverversion;
	flags = Src.flags;
}

void
Package::setName(const String& Name)
{
	name = Name;
}

void
Package::setInstalled(bool8 Installed)
{
	if (Installed)
	{
		// if it's installed, it obviously doesn't need a rsync
		flags |= PF_INSTALLED;
	}
	else
	{
		flags &= ~PF_INSTALLED;
	}
}

void
Package::setDLLName(const String& DLLName)
{
	dllname = DLLName;
}

void
Package::addDependency(PkgDependency *PkgD)
{
	if (!PkgD)
		return;

	PkgDependency* newdepends = new PkgDependency[++numDepends];
	for(uint8 i=0;i<numDepends-1;i++)
	{
		newdepends[i] = depends[i];
	}
	newdepends[numDepends-1] = *PkgD;

	delete[] depends;
	depends = newdepends;
}

uint32
Package::getDependencyVersion(const String& name)
{
	PkgDependency* dependson = getDependency(name);

	return dependson ? dependson->getRequiredVersion() : 0;
}

PkgDependency*
Package::getDependency(const String& name)
{
	for(uint8 i=0;i<numDepends;i++)
	{
		if (depends[i].getName().caseCompare(name) == 0)
			return &depends[i];
	}

	return 0;
}

Package::Package(const Package& Pkg)
{
	setName(Pkg.name);
	setDLLName(Pkg.dllname);

	this->zipsize = Pkg.zipsize;
	this->localversion = Pkg.localversion;
	this->serverversion = Pkg.serverversion;
	this->flags = Pkg.flags;
	this->numDepends = Pkg.numDepends;
	this->depends = 0;
	this->unzipsize = Pkg.unzipsize;
	if (numDepends)
	{
		this->depends = new PkgDependency[numDepends];
		for(uint8 i=0;i<numDepends;i++)
		{
			depends[i] = Pkg.depends[i];
		}
	}
}

void
Package::clearDepends()
{
	numDepends = 0;

	delete[] depends;
	depends = 0;
}

bool8
Package::allFilesInstalled()
{
	PathName filename, toCheck;
	pCStream file = (pCStream)0;
	PMHead pmh;
	uint8 buf[259], ch;
	int32 used, amt, nRead, toCheckOrigLen;

	if (!filename.set(AppPath::getManifestDir()) ||
		!filename.concat(getName()))
		goto abort;

	if (!toCheck.set(AppPath::getExeDir()))
		goto abort;
	toCheckOrigLen = toCheck.getLen();

	file = newCStream((uint16*)(const uint16*)filename, streamO_RDONLY);
	if (!file)
		goto abort;

	if (!pmh.load(file))
		goto abort;
	
	if (pmh.NumPkgs != 1 || pmh.FilenameOfs == 0)
		goto abort;

	if (streamEOF == CStreamSeek(file, pmh.FilenameOfs, streamSEEK_SET))
		goto abort;

	// read filenames & uninstall files...skip missing files
	//
	amt = used = 0;
	while (pmh.NumFilenames > 0)
	{
		if (used >= amt)
		{
			if (amt >= 256)
				amt = 0;

			nRead = CStreamRead(file, buf + amt, 256 - amt);
			if (nRead == streamEOF)
				goto abort;
			amt += nRead;
			used = 0;
		}

		if (used + buf[used] < amt)
		{
			ch = buf[used + buf[used] + 1];
			buf[used + buf[used] + 1] = '\0';

			toCheck.concat((char*)buf + used + 1);

			buf[used + buf[used] + 1] = ch;
			used += buf[used] + 1;
		}
		else
		{
            ASSERT( amt >= used );
			memmove(buf, buf + used, amt - used);
			amt -= used;
			used = amt;
			continue;
		}

		// change unix separators stored in manifest
		//
		toCheck.replace('/', Directory::sep);

		// check file
		//
		if (streamEOF == CStreamAccess(toCheck, streamF_OK))
		{
			UDEBUGMSG((TEXT("Can't find required file: %s for package: %s"), (const wchar_t*)toCheck, (const wchar_t*)getName()));
		}

		toCheck.shrink(toCheck.getLen() - toCheckOrigLen);

		--pmh.NumFilenames;
	}

	deleteCStream(file);

	return true;

abort:
	deleteCStream(file);
	return false;

}

#ifndef NORELOAD
void
Package::reload()
{
	PackageManifest pm;
	
	//the package comming from the server will not have the unzip size filled in
	//save it off and 
	uint32 unzipsize = getUnZipSize();
	uint32 servver = getServerVersion();
	bool8 updateChecked = !needsUpdateCheck();
	
	bool8 result = pm.load(this);
	if (result)
	{
		setUnZipSize(unzipsize);
		setInstalled(true);
		if (servver)
			setLocalVersion(servver);
		setUpdateChecked(updateChecked);
	}
}
#endif